package dcedit.util;

// Import Java SDK Classes
import javax.swing.*;
import javax.swing.UIManager.*;

import java.awt.*;
import java.awt.event.*;
import java.util.Map;
import java.util.HashMap;
import java.util.Observer;

// Import Application Classes
import dcedit.DCEdit;

/**
 * The DCGrid class.
 * This class is launched from the command line.
 * @author dca
 */
public class DCGrid extends JPanel {
   /** Serializable constant */
   public static final long serialVersionUID = 1305906045135L;

   /** Add image constant */
   public static final String ADD_IMAGE = Constants.IMAGE_PATH + "add-16.png";

   /** Delete image constant */
   public static final String DELETE_IMAGE = Constants.IMAGE_PATH + "cancel-16.png";

   /** Grid constants field */
   private final GridBagConstraints GRID_CONST = new GridBagConstraints();

   /** The number of columns in the grid */
   private int cols = 0;

   /** The number of rows in the grid */
   private int rows = 0;

   /** The parent container, used to call validate() if handles are enabled. */
   private Observer parent;

   /** The handles enabled field */
   private boolean handlesEnabled = false;

   /** The component map field */
   private final Map<Point,Component> COMPONENT_MAP = new HashMap<Point,Component>();

   /** Constructor */
   public DCGrid() {
      this(null,1,1);
   } // END constructor

   /** Constructor */
   public DCGrid(final Observer PARENT) {
      this(PARENT,1,1);
   } // END constructor

   /** Constructor */
   public DCGrid(final int INITIAL_COLS, final int INITIAL_ROWS) throws IllegalArgumentException {
      this(null,INITIAL_COLS,INITIAL_ROWS);
   } // END constructor

   /** Constructor */
   public DCGrid(final Observer PARENT, final int INITIAL_COLS, final int INITIAL_ROWS) throws IllegalArgumentException {
      super();
      setLayout(new GridBagLayout());

      this.parent = PARENT;

      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(0 > INITIAL_COLS) argumentErrors.append("Grid width INITIAL_COLS must be zero or positive.\n");
      if(0 > INITIAL_ROWS) argumentErrors.append("Grid height INITIAL_ROWS must be zero or positive.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::DCGrid(int,int)\n" + argumentErrors.toString());

      GRID_CONST.fill = GridBagConstraints.BOTH;
      GRID_CONST.anchor = GridBagConstraints.CENTER;
      GRID_CONST.insets = new Insets(1,1,1,1); // top,left,bottom,right
      GRID_CONST.gridwidth = 1;
      GRID_CONST.gridheight = 1;

      initGrid(INITIAL_COLS,INITIAL_ROWS);
   } // END constructor

   /**
    * The initGrid method.
    * This method invokes the methods to create the grid panel.
    */
   private void initGrid(int maxCols,int maxRows) throws IllegalArgumentException {
      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(0 > maxCols) argumentErrors.append("Grid width maxCols must be zero or positive.\n");
      if(0 > maxRows) argumentErrors.append("Grid height maxRows must be zero or positive.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::initGrid(int,int)\n" + argumentErrors.toString());

      // Create temporary row and column counts.
      int curCols = cols; // Set temporary column count based on grid columns (before any updates to the grid.)
      int curRows = rows; // Set temporary row count based on grid rows (before any updates to the grid.)

      // Update grid row and column count if necessary.
      if(maxCols > cols) cols = maxCols; // Update grid column count.
      if(maxRows > rows) rows = maxRows; // Update grid row count.

      // Initialize grid cells that are to the right and underneath existing grid cells.
      for(int rowIndex = curRows + 1 ; rowIndex <= maxRows ; ++rowIndex) {    // For each additional row.
         for(int colIndex = curCols + 1 ; colIndex <= maxCols ; ++colIndex) { // For each additional column.
            initCell(colIndex,rowIndex);                                      // Create a new label.
         } // END loop
      } // END loop

      // Initialize grid cells that are to the right of existing grid cells.
      for(int rowIndex = 1 ; rowIndex <= curRows ; ++rowIndex) {                          // For each existing row.
         for(int colIndex = curCols + 1 ; colIndex <= maxCols ; ++colIndex) {             // For each additional column.
            initCell(colIndex,rowIndex);                                                  // Create a new label.
            if(handlesEnabled) addComponentToCell(new Handle(colIndex,false),colIndex,0); // Add a handle if handles are enabled.
         } // END loop
      } // END loop

      // Initialize grid cells that are underneath existing grid cells.
      for(int rowIndex = curRows + 1 ; rowIndex <= maxRows ; ++rowIndex) {               // For each additional row.
         for(int colIndex = 1 ; colIndex <= curCols ; ++colIndex) {                      // For each existing column.
            initCell(colIndex,rowIndex);                                                 // Create a new label.
            if(handlesEnabled) addComponentToCell(new Handle(rowIndex,true),0,rowIndex); // Add a handle if handles are enabled.
         } // END loop
      } // END loop
   } // END initGrid method

   /** initCell method */
   private void initCell(int colIndex, int rowIndex) throws IllegalArgumentException {
      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(0 > colIndex) argumentErrors.append("Grid coordinate colIndex must be zero or positive.\n");
      if(0 > rowIndex) argumentErrors.append("Grid coordinate rowIndex must be zero or positive.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::initCell(int,int)\n" + argumentErrors.toString());

      final JLabel LABEL = new JLabel(/*colIndex + "," + rowIndex*/);
      LABEL.setBorder(BorderFactory.createLineBorder(LABEL.getForeground()));
      LABEL.setHorizontalAlignment(SwingConstants.CENTER);
      LABEL.setVerticalAlignment(SwingConstants.CENTER);
      addComponentToCell(LABEL,colIndex,rowIndex);
   } // END initCell method

   /** addComponentToCell method */
   public void addComponentToCell(final Component COMP, int colIndex, int rowIndex) throws IllegalArgumentException {
      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(null == COMP) argumentErrors.append("The component cannot be null.\n");
      if(0 > colIndex) argumentErrors.append("Grid coordinate colIndex must be zero or positive.\n");
      if(0 > rowIndex) argumentErrors.append("Grid coordinate rowIndex must be zero or positive.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::addComponentToCell(Component,int,int)\n" + argumentErrors.toString());

      // Initialize any grid cells that are not defined, that need to be.
      initGrid(colIndex,rowIndex);

      // Set the grid cell constants.
      GRID_CONST.gridx = colIndex;
      GRID_CONST.gridy = rowIndex;

      // Remove the current component, if it exists.
      final Point POINT = new Point(colIndex,rowIndex);
      if(COMPONENT_MAP.containsKey(POINT)) remove(COMPONENT_MAP.get(POINT));

      // Add the new component.
      add(COMP,GRID_CONST);
      COMPONENT_MAP.put(POINT,COMP);
   } // END addComponentToCell method

   /** getComponentFromCell method */
   public Component getComponentFromCell(int colIndex, int rowIndex) throws IllegalArgumentException {
      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(0 > colIndex) argumentErrors.append("Grid coordinate colIndex must be zero or positive.\n");
      if(0 > rowIndex) argumentErrors.append("Grid coordinate rowIndex must be zero or positive.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::getComponentFromCell(int,int)\n" + argumentErrors.toString());

      // Get the component map index.
      final Point POINT = new Point(colIndex,rowIndex);

      // Return the component.
      return COMPONENT_MAP.get(POINT);
   } // EDN getComponentFromCell method

   /** removeComponentFromCell method */
   public void removeComponentFromCell(int colIndex, int rowIndex) throws IllegalArgumentException {
      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(0 > colIndex) argumentErrors.append("Grid coordinate colIndex must be zero or positive.\n");
      if(0 > rowIndex) argumentErrors.append("Grid coordinate rowIndex must be zero or positive.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::removeComponentFromCell(int,int)\n" + argumentErrors.toString());

      // Remove the current component, if it exists.
      final Point POINT = new Point(colIndex,rowIndex);
      if(COMPONENT_MAP.containsKey(POINT)) remove(COMPONENT_MAP.remove(POINT));
   } // END removeComponentFromCell

   /** swapComponents method */
   public void swapComponents(int colIndex1, int rowIndex1, int colIndex2, int rowIndex2) throws IllegalArgumentException {
      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(0 > colIndex1) argumentErrors.append("Grid coordinate colIndex1 must be zero or positive.\n");
      if(0 > rowIndex1) argumentErrors.append("Grid coordinate rowIndex1 must be zero or positive.\n");
      if(0 > colIndex2) argumentErrors.append("Grid coordinate colIndex2 must be zero or positive.\n");
      if(0 > rowIndex2) argumentErrors.append("Grid coordinate rowIndex2 must be zero or positive.\n");
      if(cols < colIndex1) argumentErrors.append("Grid coordinate colIndex1 cannot be greater than the number of grid columns.\n");
      if(rows < rowIndex1) argumentErrors.append("Grid coordinate rowIndex1 cannot be greater than the number of grid rows.\n");
      if(cols < colIndex2) argumentErrors.append("Grid coordinate colIndex2 cannot be greater than the number of grid columns.\n");
      if(rows < rowIndex2) argumentErrors.append("Grid coordinate rowIndex2 cannot be greater than the number of grid rows.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::swapComponents(int,int,int,int)\n" + argumentErrors.toString());

      // Create points based on grid coordinates.
      final Point POINT1 = new Point(colIndex1,rowIndex1);
      final Point POINT2 = new Point(colIndex2,rowIndex2);

      // Proceed if there are registered components at these points.
      if(COMPONENT_MAP.containsKey(POINT1) && COMPONENT_MAP.containsKey(POINT2)) {
         // Get the components.
         final Component COMP1 = COMPONENT_MAP.get(POINT1);
         final Component COMP2 = COMPONENT_MAP.get(POINT2);

         // Remove the components from the grid.
         remove(COMPONENT_MAP.get(POINT1));
         remove(COMPONENT_MAP.get(POINT2));

         // Add the components to the grid in different locations.

         // Set the grid cell constants.
         GRID_CONST.gridx = colIndex2;
         GRID_CONST.gridy = rowIndex2;
         add(COMP1,GRID_CONST);
         COMPONENT_MAP.put(POINT2,COMP1);

         // Set the grid cell constants.
         GRID_CONST.gridx = colIndex1;
         GRID_CONST.gridy = rowIndex1;
         add(COMP2,GRID_CONST);
         COMPONENT_MAP.put(POINT1,COMP2);
      } // END if
   } // END swapComponents method

   /** getRows method */
   public int getRows() {
      return rows;
   } // END getRows method

   /** getCols method */
   public int getCols() {
      return cols;
   } // END getCols method

   /** insertRow method */
   public void insertRow(final int INDEX, final boolean AFTER) throws IllegalArgumentException {
      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(0 > INDEX) argumentErrors.append("Grid row INDEX must be zero or positive.\n");
      if(rows < INDEX) argumentErrors.append("Grid row INDEX cannot be greater than the number of grid rows.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::insertRow(int,boolean)\n" + argumentErrors.toString());

      // Initialize any grid cells that are not defined, that need to be.
      initGrid(cols,rows + 1);

      // Swap all components up 1 row.
      for(int rowIndex = rows ; (AFTER ? (rowIndex > INDEX + 1) : (rowIndex >= INDEX + 1)) ; --rowIndex) {
         for(int colIndex = cols ; colIndex >= 1 ; --colIndex) {
            swapComponents(colIndex,rowIndex,colIndex,rowIndex - 1);
         } // END loop
      } // END loop
   } // END insertRow

   /** insertCol method */
   public void insertCol(final int INDEX, final boolean AFTER) throws IllegalArgumentException {
      // Check arguments.
      StringBuilder argumentErrors = new StringBuilder();
      if(0 > INDEX) argumentErrors.append("Grid column INDEX must be zero or positive.\n");
      if(cols < INDEX) argumentErrors.append("Grid column INDEX cannot be greater than the number of grid columns.\n");
      if(!argumentErrors.toString().isEmpty()) throw new IllegalArgumentException("Error in DCGrid::insertCol(int,boolean)\n" + argumentErrors.toString());

      // Initialize any grid cells that are not defined, that need to be.
      initGrid(cols + 1,rows);

      // Swap all components up 1 row.
      for(int rowIndex = rows ; rowIndex >= 1 ; --rowIndex) {
         for(int colIndex = cols ; (AFTER ? (colIndex > INDEX + 1) : (colIndex >= INDEX + 1)) ; --colIndex) {
            swapComponents(colIndex,rowIndex,colIndex - 1,rowIndex);
         } // END loop
      } // END loop
   } // END insertCol

   /** appendRow method */
   public void appendRow() {
      // Initialize any grid cells that are not defined, that need to be.
      initGrid(cols,rows + 1);
   } // END appendRow

   /** appendCol method */
   public void appendCol() {
      // Initialize any grid cells that are not defined, that need to be.
      initGrid(cols + 1,rows);
   } // END appendCol

   /** prependRow method */
   public void prependRow() {
      // Initialize any grid cells that are not defined, that need to be.
      initGrid(cols,rows + 1);

      // Swap all components up 1 row.
      for(int rowIndex = rows ; rowIndex > 1 ; --rowIndex) {
         for(int colIndex = cols ; colIndex >= (handlesEnabled ? 0 : 1) ; --colIndex) {
            swapComponents(colIndex,rowIndex,colIndex,rowIndex - 1);
         } // END loop
      } // END loop
   } // END prependRow

   /** prependCol method */
   public void prependCol() {
      // Initialize any grid cells that are not defined, that need to be.
      initGrid(cols + 1,rows);

      // Swap all components up 1 columns.
      for(int rowIndex = rows ; rowIndex >= (handlesEnabled ? 0 : 1) ; --rowIndex) {
         for(int colIndex = cols ; colIndex > 1 ; --colIndex) {
            swapComponents(colIndex,rowIndex,colIndex - 1,rowIndex);
         } // END loop
      } // END loop
   } // END prependCol

   /** deleteRow method */
   public void deleteRow(int index) {
      if(rows > 1) {
         // Swap all components up 1 row.
         for(int rowIndex = index + 1 ; rowIndex <= rows ; ++rowIndex) {
            for(int colIndex = 1 ; colIndex <= cols ; ++colIndex) {
               swapComponents(colIndex,rowIndex,colIndex,rowIndex - 1);
            } // END loop
         } // END loop

         // Remove all components within the final row.
         for(int colIndex = (handlesEnabled ? 0 : 1) ; colIndex <= cols ; ++colIndex) {
            final Point POINT = new Point(colIndex,rows);
            remove(COMPONENT_MAP.remove(POINT));
         } // END loop

         // Decrement row count
         rows--;
      } // END if
   } // END deleteRow

   /** deleteCol method */
   public void deleteCol(int index) {
      if(cols > 1) {
         // Swap all components up 1 columns.
         for(int rowIndex = 1 ; rowIndex <= rows  ; ++rowIndex) {
            for(int colIndex = index + 1 ; colIndex <= cols  ; ++colIndex) {
               swapComponents(colIndex,rowIndex,colIndex - 1,rowIndex);
            } // END loop
         } // END loop

         // Remove all components within the final column.
         for(int rowIndex = (handlesEnabled ? 0 : 1) ; rowIndex <= rows ; ++rowIndex) {
            final Point POINT = new Point(cols,rowIndex);
            remove(COMPONENT_MAP.remove(POINT));
         } // END loop

         // Decrement column count
         cols--;
      } // END if
   } // END deleteCol

   /** setHandlesEnabled method */
   public void setHandlesEnabled(boolean handlesEnabled) {
      this.handlesEnabled = handlesEnabled;

      // Set row handles
      for(int rowIndex = 1 ; rowIndex <= rows ; ++rowIndex) {
         if(handlesEnabled) addComponentToCell(new Handle(rowIndex,true),0,rowIndex);
         else removeComponentFromCell(0,rowIndex);
      } // END loop

      // Set column handles
      for(int colIndex = 1 ; colIndex <= cols ; ++colIndex) {
         if(handlesEnabled) addComponentToCell(new Handle(colIndex,false),colIndex,0);
         else removeComponentFromCell(colIndex,0);
      } // END loop

      if(isVisible()) updateUI();
   } // END setHandlesEnabled

   /** getComponentMap method */
   public Map<Point,Component> getComponentMap() {
      return COMPONENT_MAP;
   } // END getComponentMap

   /** Inner enum grid action which will tell the client what type of update has been applied to the grid. */
   public enum GridAction {
      INSERT,
      DELETE,
      NONE
   } // END enum GridAction

   /** Inner enum grid dimension which will tell the client what has been updated, when a handle has been clicked. */
   public enum GridDimension {
      COLUMN,
      ROW,
      NONE
   } // END enum GridDimension

   /** Inner class handle update info which contains information as to what column or row has been updated. */
   public class HandleUpdateInfo {
      /** Grid Action field which tells what type of handle action has been performed. */
      private GridAction action;

      /** Grid Dimension field which tells which dimension of the grid has been affected. */
      private GridDimension dimension;

      /** Number field which tells which position in the grid has been updated. */
      private int positionNumber;

      /** Constructor */
      public HandleUpdateInfo() {
         setAction(GridAction.NONE);
         setDimension(GridDimension.NONE);
         setPositionNum(-1);
      } // END constructor

      /** Returns the GridAction field */
      public GridAction getAction() { return this.action; }

      /** Sets the GridAction field */
      public void setAction(GridAction action) { this.action = action; }

      /** Returns the GridDimension field */
      public GridDimension getDimension() { return this.dimension; }

      /** Sets the GridDimension field */
      public void setDimension(GridDimension dimension) { this.dimension = dimension; }

      /** Returns the position number field */
      public int getPositionNum() { return this.positionNumber; }

      /** Sets the position number field */
      public void setPositionNum(int positionNumber) { this.positionNumber = positionNumber; }
   } // END class HandleUpdateInfo

   /** This handle update info field tells the client what column or row has been updated. */
   public final HandleUpdateInfo HANDLE_UPDATE_INFO = new HandleUpdateInfo();

   /** Inner class handle */
   public class Handle extends JPanel {
      /** Serial Version UID version for serializable classes */
      public static final long serialVersionUID = 1306248220643L;

      /** Constructor */
      public Handle(final int POS, final boolean VERTICAL) {
         // Create strings.
         final String TYPE = (VERTICAL ? "column" : "row");
         final String TOOLTIP_TEXT = "Click to add a " + TYPE + " [WHERE] this " + TYPE + ".";

         // Create the delete button
         final OmniImage DELETE_BTN = new OmniImage(DCEdit.class.getResource(DELETE_IMAGE));
         DELETE_BTN.setToolTipText("Click to delete this " + TYPE);
         DELETE_BTN.setBorder(BorderFactory.createLineBorder(DELETE_BTN.getForeground()));
         DELETE_BTN.setHorizontalAlignment(SwingConstants.CENTER);
         DELETE_BTN.setVerticalAlignment(SwingConstants.CENTER);

         // Create the insert before button
         final OmniImage INSERT_BEFORE_BTN = new OmniImage(DCEdit.class.getResource(ADD_IMAGE));
         INSERT_BEFORE_BTN.setToolTipText(TOOLTIP_TEXT.replace("[WHERE]","before"));
         INSERT_BEFORE_BTN.setBorder(BorderFactory.createLineBorder(INSERT_BEFORE_BTN.getForeground()));
         INSERT_BEFORE_BTN.setHorizontalAlignment(SwingConstants.CENTER);
         INSERT_BEFORE_BTN.setVerticalAlignment(SwingConstants.CENTER);

         // Create the insert after button
         final OmniImage INSERT_AFTER_BTN = new OmniImage(DCEdit.class.getResource(ADD_IMAGE));
         INSERT_AFTER_BTN.setToolTipText(TOOLTIP_TEXT.replace("[WHERE]","after"));
         INSERT_AFTER_BTN.setBorder(BorderFactory.createLineBorder(INSERT_AFTER_BTN.getForeground()));
         INSERT_AFTER_BTN.setHorizontalAlignment(SwingConstants.CENTER);
         INSERT_AFTER_BTN.setVerticalAlignment(SwingConstants.CENTER);

         // Create the delete mouse listener
         DELETE_BTN.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent me) {
               if(VERTICAL) deleteRow(POS);
               else deleteCol(POS);

               HANDLE_UPDATE_INFO.setAction(GridAction.DELETE);
               HANDLE_UPDATE_INFO.setDimension(VERTICAL ? GridDimension.ROW : GridDimension.COLUMN);
               HANDLE_UPDATE_INFO.setPositionNum(POS);
               if(null != parent) parent.update(null,null);
               HANDLE_UPDATE_INFO.setAction(GridAction.NONE);
               HANDLE_UPDATE_INFO.setDimension(GridDimension.NONE);
               HANDLE_UPDATE_INFO.setPositionNum(-1);

               if(isVisible()) updateUI();
            } // END mouseClicked
         });

         // Create the insert before mouse listener
         INSERT_BEFORE_BTN.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent me) {
               if(VERTICAL) insertRow(POS,false);
               else insertCol(POS,false);

               HANDLE_UPDATE_INFO.setAction(GridAction.INSERT);
               HANDLE_UPDATE_INFO.setDimension(VERTICAL ? GridDimension.ROW : GridDimension.COLUMN);
               HANDLE_UPDATE_INFO.setPositionNum(POS);
               if(null != parent) parent.update(null,null);
               HANDLE_UPDATE_INFO.setAction(GridAction.NONE);
               HANDLE_UPDATE_INFO.setDimension(GridDimension.NONE);
               HANDLE_UPDATE_INFO.setPositionNum(-1);

               if(isVisible()) updateUI();
            } // END mouseClicked
         });

         // Create the insert after mouse listener
         INSERT_AFTER_BTN.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent me) {
               if(VERTICAL) insertRow(POS,true);
               else insertCol(POS,true);

               HANDLE_UPDATE_INFO.setAction(GridAction.INSERT);
               HANDLE_UPDATE_INFO.setDimension(VERTICAL ? GridDimension.ROW : GridDimension.COLUMN);
               HANDLE_UPDATE_INFO.setPositionNum(POS + 1);
               if(null != parent) parent.update(null,null);
               HANDLE_UPDATE_INFO.setAction(GridAction.NONE);
               HANDLE_UPDATE_INFO.setDimension(GridDimension.NONE);
               HANDLE_UPDATE_INFO.setPositionNum(-1);

               if(isVisible()) updateUI();
            } // END mouseClicked
         });

         // Set the layout direction
         final JPanel WRAPPER_PANEL = new JPanel();
         WRAPPER_PANEL.setLayout(new BoxLayout(WRAPPER_PANEL,(VERTICAL ? BoxLayout.Y_AXIS : BoxLayout.X_AXIS)));

         // Add button components to the panel
         WRAPPER_PANEL.add(INSERT_BEFORE_BTN);
         WRAPPER_PANEL.add(DELETE_BTN);
         WRAPPER_PANEL.add(INSERT_AFTER_BTN);
         add(WRAPPER_PANEL);
      } // Constructor
   } // END inner class Handle

   /**
    * The main class method.
    * @param ARGS The command line arguments.
    *        Known arguments:
    *              (none)
    */
   public static void main(final String[] ARGS) {
      final JFrame FRAME = new JFrame("DCGrid");
      final DCGrid GRID = new DCGrid(5,5);

      // Set Look and Feel Decorated.
      JFrame.setDefaultLookAndFeelDecorated(true);

      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            try {
               for(LookAndFeelInfo lafi : UIManager.getInstalledLookAndFeels()) {
                  // Set the Look and Feel.
                  if("Nimbus".equals(lafi.getName())) {
                     UIManager.setLookAndFeel(lafi.getClassName());
                  } // END if
               } // END look and feel loop
            } catch (Exception e) {
               System.out.println("Nimbus Look and Feel failed to initialize.");
            } // END try/catch

            // Build the application
            GRID.setHandlesEnabled(true);
            FRAME.add(GRID);
            FRAME.pack();
            FRAME.setSize(new Dimension(500,500));
            FRAME.setMinimumSize(new Dimension(500,500));
            FRAME.setPreferredSize(new Dimension(500,500));
            FRAME.setLocationRelativeTo(null);
            FRAME.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            // Display the application.
            FRAME.setVisible(true);
         } // END run
      });
   } // END static main.
} // END class DCGrid